//------------------------------------------------------------------------------
///
///	\file sm-auto.c
///
///	\brief
///		state machine auto test
///
///		In this test, the state machine is advanced automatically based
///		on two inputs: (1) the current state (2) the current event.
///		The table sm_auto  contains all possible combinations of these
///		two variables.  The function SM_StateAuto() manages the state
///		"automatically".  The code in each function can focus on
///		processing the state and not worry about managing the state.
///
//	$Id: sm-auto.c 330 2015-02-02 05:57:58Z ac.verbeck@gmail.com $
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//	The MIT License (MIT)
//
//	Copyright (c) 2014 A.C. Verbeck
//
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.

//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//	THE SOFTWARE.
//------------------------------------------------------------------------------

#include <stdio.h>
#include <stdint.h>									//	Standard int types
#include <stdbool.h>								//	Bool

#include "sm.h"
#include "debug.h"

//------------------------------------------------------------------------------
// Local enum
//------------------------------------------------------------------------------
enum {
	ST_STANDBY	= 0,
	ST_RUN		= 1,
	ST_MENU		= 2,
	ST_DISPLAY	= 3,
	ST_CT		= 4,
};

enum {
	EV_POWER	= 0,
	EV_MENU		= 1,
	EV_DISPLAY	= 2,
	EV_BUTTON	= 3,
	EV_CT		= 4,
};

//------------------------------------------------------------------------------
// Local functions
//------------------------------------------------------------------------------
static SM_STATE sm_test_init(STATE_MACHINE* sm);

static SM_STATUS s0_st_enter(STATE_MACHINE* sm, SM_EVENT ev);
static SM_STATUS s0_st_process(STATE_MACHINE* sm, SM_EVENT ev);
static SM_STATUS s0_st_exit(STATE_MACHINE* sm, SM_EVENT ev);

static SM_STATUS s1_st_enter(STATE_MACHINE* sm, SM_EVENT ev);
static SM_STATUS s1_st_process(STATE_MACHINE* sm, SM_EVENT ev);
static SM_STATUS s1_st_exit(STATE_MACHINE* sm, SM_EVENT ev);

static SM_STATUS s2_st_enter(STATE_MACHINE* sm, SM_EVENT ev);
static SM_STATUS s2_st_process(STATE_MACHINE* sm, SM_EVENT ev);
static SM_STATUS s2_st_exit(STATE_MACHINE* sm, SM_EVENT ev);

static SM_STATUS s3_st_enter(STATE_MACHINE* sm, SM_EVENT ev);
static SM_STATUS s3_st_process(STATE_MACHINE* sm, SM_EVENT ev);
static SM_STATUS s3_st_exit(STATE_MACHINE* sm, SM_EVENT ev);

//------------------------------------------------------------------------------
// Local data
//------------------------------------------------------------------------------
static const SM_STATE_FN states[] = {
	[ST_STANDBY]	= {	s0_st_enter,	s0_st_process,		s0_st_exit		},	//	state: s0 -- standby state
	[ST_RUN]		= {	s1_st_enter,	s1_st_process,		s1_st_exit		},	//	state: s1 -- run state
	[ST_MENU]		= {	s2_st_enter,	s2_st_process,		s2_st_exit		},	//	state: s2 -- menu state
	[ST_DISPLAY]	= {	s3_st_enter,	s3_st_process,		s3_st_exit		},	//	state: s3 -- display state
};
static const uint32_t sm_state_ct = sizeof(states)/sizeof(SM_STATE_FN);

static const SM_STATE sm_auto[ST_CT][EV_CT] = {
//						EV_POWER			EV_MENU				EV_DISPLAY			EV_BUTTON
	[ST_STANDBY]	= {	ST_RUN,				ST_STANDBY,			ST_STANDBY,			ST_STANDBY	},
	[ST_RUN]		= {	ST_STANDBY,			ST_MENU,			ST_DISPLAY,			ST_RUN		},
	[ST_MENU]		= {	ST_STANDBY,	   		ST_RUN,				ST_DISPLAY,			ST_MENU		},
	[ST_DISPLAY]	= {	ST_STANDBY,			ST_MENU,			ST_RUN,				ST_DISPLAY	},
//						EV_POWER			EV_MENU				EV_DISPLAY			EV_BUTTON
};
static STATE_MACHINE sm = {
	.InitFn			= sm_test_init,												//	fn: initialization
	.x				= ST_CT,													//	auto array: ST_CT
	.y				= EV_CT,													//	auto array: EV_CT
};

//------------------------------------------------------------------------------
//!	@brief
//!		Next test state machine
//!		Init the state machine
//!
//!	@param
//!		none
//!
//!	@return
//!		none
//------------------------------------------------------------------------------
void sm_auto_init(void)
{
	SM_Init(&sm, states, sm_state_ct);
}

//------------------------------------------------------------------------------
//!	@brief
//!		Next test state machine
//!		Run the state machine -- process incoming events
//!		This isn't the best way to do this.
//!		This is done to demonstrate the SM_StateAuto function.
//!
//!	@param
//!		int ev:	incoming event
//!
//!	@return
//!		none
//------------------------------------------------------------------------------
void sm_auto_process(int ev_in)
{
	int ev_proc;

	switch (ev_in) {
	case 'p':
		ev_proc = EV_POWER;
		break;																	//	p = power event
	case 'm':
		ev_proc = EV_MENU;
		break;																	//	m = menu event
	case 'd':
		ev_proc = EV_DISPLAY;
		break;																	//	d = display event
	default:
		ev_proc = EV_BUTTON;
		break;																	//	default is button
	}

	SM_StateAuto(&sm, sm_auto, ev_proc);	 									//	Look up auto state
	SM_Process(&sm, states, (SM_EVENT)ev_in);									//	Process current state then, go to auto state
}

//------------------------------------------------------------------------------
//!	@brief
//!		State machine state processing
//!		Initialize the state machine, return the first state
//!
//!	@param
//!		sm:	state machine
//!		ev:	state machine event
//!
//!	@return
//!		state machine first state
//------------------------------------------------------------------------------
static SM_STATE sm_test_init(STATE_MACHINE* sm)
{
	TRACE(INFO, "auto state machine test");
	return ST_STANDBY;
}

//------------------------------------------------------------------------------
//!	@brief
//!		State machine state processing
//!		S0 state processing: This is the standby state.
//!		- s0_st_enter:		enter state
//!		- s0_st_process:	process state
//!		- s0_st_exit:		exit state
//!
//!	@param
//!		sm:	state machine
//!		ev:	state machine event
//!
//!	@return
//!		state machine status
//------------------------------------------------------------------------------
static SM_STATUS s0_st_enter(STATE_MACHINE* sm, SM_EVENT ev)
{
	TRACE(INFO, "standby enter");												//	Standby state entry
	return SM_OK;																//	Status is OK
}
static SM_STATUS s0_st_process(STATE_MACHINE* sm, SM_EVENT ev)
{
	TRACE(INFO, "standby process");												//	Standby state processing
	return SM_OK;																//	Status is OK
}
static SM_STATUS s0_st_exit(STATE_MACHINE* sm, SM_EVENT ev)
{
	TRACE(INFO, "standby exit");												//	Standby state exit
	return SM_OK;																//	Status is OK
}

//------------------------------------------------------------------------------
//!	@brief
//!		State machine state processing
//!		S1 state processing: This is the run state.
//!		- s1_st_enter:		enter state
//!		- s1_st_process:	process state
//!		- s1_st_exit:		exit state
//!
//!	@param
//!		sm:	state machine
//!		ev:	state machine event
//!
//!	@return
//!		state machine status
//------------------------------------------------------------------------------
static SM_STATUS s1_st_enter(STATE_MACHINE* sm, SM_EVENT ev)
{
	TRACE(INFO, "run enter"); 													//	Run state entry
	return SM_OK;																//	Status is OK
}
static SM_STATUS s1_st_process(STATE_MACHINE* sm, SM_EVENT ev)
{
	TRACE(INFO, "run process");													//	Run state processing
	return SM_OK;																//	Status is OK
}
static SM_STATUS s1_st_exit(STATE_MACHINE* sm, SM_EVENT ev)
{
	TRACE(INFO, "run exit");													//	Run state exit
	return SM_OK;																//	Status is OK
}

//------------------------------------------------------------------------------
//!	@brief
//!		State machine state processing
//!		S2 state processing: This is the menu state.
//!		- s2_st_enter:		enter state
//!		- s2_st_process:	process state
//!		- s2_st_exit:		exit state
//!
//!	@param
//!		sm:	state machine
//!		ev:	state machine event
//!
//!	@return
//!		state machine status
//------------------------------------------------------------------------------
static SM_STATUS s2_st_enter(STATE_MACHINE* sm, SM_EVENT ev)
{
	TRACE(INFO, "menu enter");													//	Menu state entry
	return SM_OK;																//	Status is OK
}
static SM_STATUS s2_st_process(STATE_MACHINE* sm, SM_EVENT ev)
{
	TRACE(INFO, "menu process");												//	Menu state process
	return SM_OK;																//	Status is OK
}
static SM_STATUS s2_st_exit(STATE_MACHINE* sm, SM_EVENT ev)
{
	TRACE(INFO, "menu exit");													//	Menu state exit
	return SM_OK;																//	Status is OK
}

//------------------------------------------------------------------------------
//!	@brief
//!		State machine state processing
//!		S3 state processing: This is the display state.
//!		- s3_st_enter:		enter state
//!		- s3_st_process:	process state
//!		- s3_st_exit:		exit state
//!
//!	@param
//!		sm:	state machine
//!		ev:	state machine event
//!
//!	@return
//!		state machine status
//------------------------------------------------------------------------------
static uint16_t idx = 0;
static SM_STATUS s3_st_enter(STATE_MACHINE* sm, SM_EVENT ev)
{
	TRACE(INFO, "display enter");												//	Display state entry
	return SM_OK;																//	Status is OK
}
static SM_STATUS s3_st_process(STATE_MACHINE* sm, SM_EVENT ev)
{
	TRACE(INFO, "display process");												//	Display state process
	printf("%02x ",(char)ev);
	if (idx++ == 16) printf("\n");
	return SM_OK;																//	Status is OK
}
static SM_STATUS s3_st_exit(STATE_MACHINE* sm, SM_EVENT ev)
{
	TRACE(INFO, "display exit");												//	Display state exit
	return SM_OK;																//	Status is OK
}

//
//  End: sm-auto.c
//
